class BaseModel
  attr_accessor :table_name, :attributes, :hash_set
  def initialize attributes
    @table_name = self.class.to_s
    @attributes = attributes
    @connection = ActiveRecord::Base.connection
  end

  def get_info
    "table_name: #{@table_name};\nattributes: #{@attributes}"
  end

  def all
    sql = "SELECT * FROM #{@table_name}"
    @hash_set = @connection.select_all sql
    get_record_sets
  end

  def get_record_sets actual_attr = nil
    record_sets = []
    actual_attr ||= @attributes
    @hash_set.each do |row|
      rs = RecordSet.new
      # Adding dynamically getters and setters to recordset object
      actual_attr.each do |attribute|
        eval ("
          def rs.#{attribute}
            @#{attribute}
          end
          def rs.#{attribute}= attr
            @#{attribute}=attr
          end
          ")
      end
      # populating object with data from hash
      row.each do |key,value|
        rs.send("#{key}=",value)
      end
      record_sets << rs
    end
    record_sets
  end

  def find params
    is_ms_sql = @connection.adapter_name.downcase == "sqlserver"
    actual_attrs = @attributes 
    raise "not a hash" unless params.instance_of? Hash
    sql = "SELECT"
    do_pagination = false # specify if the pagination will take place
    # pass integer for TOP sql command
    # example :limit => 10
    # or pass an array for sqlserver pagination
    # example :limit => [2,7]

    if params.has_key?(:limit) && is_ms_sql
      limit = params[:limit]
      raise "not a integer nor an array" unless limit.instance_of?(Array) || limit.instance_of?(Fixnum)
      if limit.instance_of?(Fixnum)
        sql.concat(" TOP #{limit}")
      elsif limit.instance_of?(Array)
        raise ":order must be specified" unless params.has_key? :order
        do_pagination = true
      end
    end

    # pass array of values(Strings or Symbols) for selecting only some columns
    # example :only => [:id, :firstname]
    # or do not specify :only at all for selecting all columns
    if params.has_key? :only
      actual_attrs = params[:only]
      raise "not an array" unless actual_attrs.instance_of? Array
      sql.concat(" #{actual_attrs.join(", ")}")
    else
      sql.concat(" *")
    end
    # if pagination needed - some bullshit manipulation with select sql statement
    sql.concat(", ROW_NUMBER() OVER (ORDER BY #{params[:order]}) AS 'RowNumber'") if do_pagination
    sql.concat(" FROM #{@table_name}")
    # pass a string for simple condition
    # or pass an array for complex conditions
    if params.has_key? :conditions
      conditions = params[:conditions]
      raise "not a string nor an array" unless conditions.instance_of?(Array) || conditions.instance_of?(String)
      if conditions.instance_of?(Array)
          conditions_str = conditions.shift
          conditions.each do |param|
              conditions_str.sub!("?", param)
          end
          sql.concat(" WHERE #{conditions_str}")
      elsif conditions.instance_of?(String) && !conditions.empty?
          sql.concat(" WHERE #{conditions}")
      end
    end
    # pass order for ORDER BY
    # MUST BE for pagination
    if params.has_key? :order
      order = params[:order]
      raise "not a string" unless order.instance_of? String
      sql.concat(" ORDER BY #{order}") unless do_pagination
    end

    if params.has_key?(:limit) && !is_ms_sql
      limit = params[:limit]
      raise "not a integer nor an array" unless limit.instance_of?(Array) || limit.instance_of?(Fixnum)
      if limit.instance_of?(Fixnum)
        sql.concat(" LIMIT #{limit}")
      elsif limit.instance_of?(Array)
        raise ":order must be specified" unless params.has_key? :order
        sql.concat(" LIMIT #{limit[1].to_i - limit[0].to_i + 1} OFFSET #{limit[0].to_i - 1}")
      end
    end

    # continuation of bullshit manipulation with select sql statement for pagination
    sql = "WITH pagination AS ( #{sql} ) SELECT * FROM pagination WHERE RowNumber BETWEEN #{params[:limit][0]} AND #{params[:limit][1]}" if do_pagination
    sql.concat ";"
    
    #raise sql.inspect
    @hash_set = @connection.select_all sql
    #raise @hash_set.inspect
    
    actual_attrs << "RowNumber" if do_pagination
    get_record_sets actual_attrs
  end

end